########################################################################
#  Pyfm - personal finance data-base
#  Copyright (C) 2007,8 Ehud Ben-Reuven
#  udi@benreuven.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
########################################################################
"""Export flat lines"""
from obj.obj import db
from obj.dt import DT
from obj.flat import stagvalue,add2line,sobj,sdict,line_merge
from obj.objtools import get_filter_tags,get_command_tags
from model.model import *
from model.modellib import *
from model.trnlib import *
from model.tagsyntax import *
from lib.quoteinterface import getFX
import copy
from lib import conf
def export_build_line(o):
    """Return a flat line in dictionary form representing o without FILE=_MERGED_
"""
    assert isinstance(o,Obj)
    line={}
    o.build_line(line)

    if 'FILE' in line and line['FILE'][0]=='_MERGED_': del line['FILE']
    # The original NLINE number must remain because here it is part of a key to
    # an object and may be used in several lines on the output to identify the
    # same object.
    for i,v in enumerate(line.get('NLINE',[])):
        line['NLINE'][i]=v.replace('_MERGED_','_EXPORT_')

    return line

def export_flat_obj(fout,o,once,going_to_be_represented=False):
    """Write into fout, the content of 'o' as a flat lines.
Any value which is an Obj will also be recursively written into the file.
once is a list of objects that where already written to fout, you should start with once=[].
FILE=_MERGED_ and tags that starts with '_' are not written
If o is an Obj then each line begins with a key to the object followed by one of its tag-value pairs
If o is a dictionary then take it to be a single line that should be flatten and represented as a single line.
"""
    if isinstance(o,list):
        for v in o:
            export_flat_obj(fout,v,once,going_to_be_represented)
        return
    elif isinstance(o,Obj):
        # Make sure we will not write the same object twice
        if o in once: return
        once.append(o)
        # Every flat line will begin with a key to the object.
        key=export_build_line(o)
        tagvalues=o.dict()
    elif isinstance(o,dict):
        # If o is a dict then it is a line that needs to be flatten into a SINGLE line.
        key={} # This is an indication of a single flat line
        tagvalues=o
        for t in o:
            # validate that objects from same class appear only once
            if any(isinstance(v,Obj) for v in o[t]):
                assert len(o[t])==1
    else:
        raise Exception('bad type')
    # Represent o itself if it was not yet represented (as a key) and not going to be represented (as a value) somewhere else.
    lastline={}
    if not going_to_be_represented and key: lastline=key # Ensure that the object itself is represented at least once
    for t in tagvalues:
        if t.startswith('_'): continue
        if t=='FILE' and '_MERGED_' in tagvalues[t]: continue # FILE tag should be single valued
        if t=='NLINE' and any(v.startswith('_MERGED_') for v in tagvalues[t]): continue
        for v in tagvalues[t]:
            if isinstance(v,Obj):
                export_flat_obj(fout,v,once,going_to_be_represented=True)
                line=export_build_line(v)
            else:
                line={t:[v]}
            if not key: # Single flat line
                line_merge(lastline,line)
            # Make sure line is not completely coverd by key
            elif any(t not in key or line[t][0] not in key[t] for t in line): # line is a key (to v) so each tag has one value
                line_merge(line,key)
                lastline={} # key was represented
                print >>fout,sdict(line)
    if lastline: # Terminate single flat line
        print >>fout,sdict(lastline)
    
def export_flat(fout,dtstart,dtend,line):
    """For every holding export all merged transactions between dtstart and dtend,
in addition export buy/sell merged transactions before dtstart if the holding had a non-zero position at dtstart.
In addition to exporting the transactions, export all releated objects from the exported transactions are made"""
    print >>fout,stagvalue('FILECLASS','BASE'),stagvalue('DTEND',dtend),stagvalue('DTSTART',DT('PAST')),stagvalue('FULL','TRUE'),stagvalue('COMPLETE','TRUE')
    once=[]
    transaction_filter=gettransaction_filter(line,dtre=dtend)
    for h in getholdings(line):
        #print h
        trans=gettransactions(transaction_filter,h)
        if not trans: continue
        startpos=sum(t.UNITS[0] if t.UNITS else 0. for t in trans if t.DTSETTLE[0]<dtstart)
        instart=1e-6<abs(startpos)
        if instart and h.ischecking():
            # Generate a single line that will represnt an opening balance transaction
            line={'Holding':[h],
                  'DTSETTLE':[max(t.DTSETTLE[0] for t in trans if t.DTSETTLE[0]<dtstart)],
                  'TRNTYPE':['OPENING_BALANCE'],
                  'UNITS':[startpos]}
            export_flat_obj(fout,line,once)
        inrange=False
        for t in trans:
            if any(fn!='_MERGED_' for fn in t['HoldingSource'][0]['FILE']):
                perr('Transaction must be _MERGED_',t)
            if t.DTSETTLE[0]>=dtstart: inrange=True
            if t.DTSETTLE[0]>=dtstart or ( # All inrange transactions are exported
                instart and # all buy/sell transactions that made the start position are also exported TODO: remove sell by taking out some of buy
                (not h.ischecking()) and # we already summed up all instart transactions in a single opening-balance-transaction
                t.UNITS and t.UNITS[0]):
                export_flat_obj(fout,t,once)
        # price History
        history=[]
        if instart:
            # save the latest price history just before dtstart
            history+=db.getallobjects(History,{'!DTASOF':['MAX'],
                                              '<=DTASOF':[dtstart],
                                              'Asset':h['Asset']})  # Its possible to have more than one Asset, which is OK because they will be ORed
        if (inrange or instart) and dtend>dtstart:
            history+=db.getallobjects(History,{'>=DTASOF':[dtstart],
                                               '<DTASOF':[dtend],
                                               'Asset':h['Asset']})  # Its possible to have more than one Asset, which is OK because they will be ORed
        export_flat_obj(fout,history,once)
